package com.sff.site.abstract {
	
	
	import com.sff.core.sff_internal;
	import com.sff.site.core.INode;
	import com.sff.site.core.IPath;
	import com.sff.site.events.FlowEventDispatcher;
	import com.sff.site.events.NodeEvent;
	import com.sff.site.node.NodeDescriptor;
	import com.sff.site.utils.StringUtility;

	import flash.net.URLVariables;
	import flash.utils.Dictionary;	
	

	public class AbstractNode extends FlowEventDispatcher implements INode{
		
		
		public function getDefaultId() : String {
			return _default;
		}
		
		sff_internal function setDefaultId( id : String ) : void {
			if( _default == id ) return;
			_default = id;
			dispatchEvent( new NodeEvent( NodeEvent.DEFAULT_CHANGE, this, false ) );
		}
		
		public function getId() : String {
			return _id;
		}
		
		/** * @private */
		public function activate( params : URLVariables = null ) : void {
			// abstract
		}

		/** * @private */
		public function isActive() : Boolean {
			// abstract
			return false;
		}
		
		/** * @private */
		public function getCurrentChild() : INode {
			return null;
		}
		
		
		public function AbstractNode( parent : AbstractNode ) {
			super( );
			_parent = parent;
			_childs = new Array( );
			_cMap 	= new Dictionary( true );
		}
		
		public function getParams() : URLVariables {
			return _params;
		}
		
		public function setParams(params : URLVariables) : void {
			_params = params;
		}
		
		
		
		/**
		 * add a child node to this one.
		 * if a node with the same id exist the node isn't added and the method return the current node;
		 * 
		 * @return Node true if the node is added, false otherwise
		 */
		public function addChild( node : INode ) : INode {
			var current : AbstractNode = _cMap[ node.getId() ];
			if( current != null ) {
				if( ! current.getDefaultId() ) current.sff_internal::setDefaultId(  node.getDefaultId() );
				return current;
			}
			_childs.push( node );
			_cMap[ node.getId() ] = node;
			sff_internal::fireCapture( new NodeEvent( NodeEvent.CHILD_ADDED, this, true ) );
			sff_internal::fireBubbling( new NodeEvent( NodeEvent.CHILD_ADDED, this ) );
			return node;
		}
		
		public function getChilds() : Array {
			return  _childs;
		}

		/**
		 * return the child node match with the given id.
		 */
		public function getChild( id : String ) : INode {
			var res : INode = _cMap[ id ];
			if( !res )
				throw new Error( "AbstractNode - getChild : no child with the given nodeId '"+id+"' in node '" + _id + "'" );
			return _cMap[ id ];
		}
		/**
		 * 
		 */
		public function hasChild( id : String ) : Boolean {
			return ( _cMap[ id ] != undefined );
		}
		
		//TODO cacher cette methode
		public function describe(descriptor : NodeDescriptor) : void {
			
		}
		
		public function path( getDefaults : Boolean = false ) : IPath {
			return null;
		}

		public function parent() : INode {
			return _parent;
		}

		
		
		public function stringify( indent : int = 0) : String {
			var tab : String = StringUtility.multiply( "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", indent );
			var str : String = "";
			var params : String = "";
			if( _params ) params = _params.toString();
			
			if( isActive() )
				str += tab+"<b>id : "+_id+"  </b><i>" + params + "</i>";
			else
				str += tab+"id : "+_id+"<i>" + params + "</i>";
			
			if( _default ) 
				str += "<i>" + _default + "</i>";
			
			str += "</br>";
			
			
			for each (var iNode : INode in _childs) {
				str += tab+iNode.stringify( indent +1 );
			}
			return str;
		}
		
		
		
		sff_internal function fireCapture( event : NodeEvent ) : void {
			if( _parent )
				_parent.sff_internal::fireCapture( event );
			dispatchEvent( event );
		}
		
		sff_internal function fireBubbling( event : NodeEvent ) : void {
			dispatchEvent( event );
			if( _parent )
				_parent.sff_internal::fireBubbling( event );
		}

		
		
		protected function _dispose() : void {
			while( _childs.length > 0 ) _childs.pop().sff_internal::dispose( );
			_childs = null;
			_parent = null;
			//_id = null;
			_cMap = null;
		}
		
		/**
		 * go down in nodes tree to find the defaults
		 * return this if no defaults
		 */
		public function getDefaultNode() : INode {
			if( _default && hasChild( _default ) )
				return getChild( _default ).getDefaultNode();
			return this;	
		}


		
		/**
		 * represent the name of the node.
		 */
		protected var _id : String;

		/**
		 * teh parent of this node.
		 * can be empty if the node is root node.
		 */
		protected var _parent : AbstractNode;
		
		protected var _params 	: URLVariables;
		
		/**
		 * the default child id
		 */
		protected var _default 	: String;
		
		/**
		 * Childs of this node
		 * Can be empty if node is a leaf
		 */
		protected var _childs : Array/*AbstractNode*/;
		
		/**
		 * map of childs nodes by childs nodes id's
		 */
		protected var _cMap : Dictionary/*String,AbstractNode*/;
		
	}
}
